﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Assembler_Compiler
{
    class Line
    {
        private bool isComment;                
        private Int16 address;
        private Int16 byteLength;
        private string commandName;
        private string commandFormat;
        private Int16 w;
        private string[] operands;

        public Line(string line, Int16 address)
        {
            String[] commandAndCommentStrings = line.Split(';');
            if (commandAndCommentStrings[0].Replace(" ", "") == "")
            {
                this.isComment = true;
            }
            else
            {
                this.isComment = false;
                this.address = address;
                String[] commandStrings = getCommandStrings(commandAndCommentStrings[0]);
                if (commandStrings.Length > 3)
                {
                    throw new Exception("Bad Input line: " + commandAndCommentStrings[0]);
                }

                if (!OPTAB.IsCommand(commandStrings[0]))
                {
                    // is reserved function
                    if (!OPTAB.IsCommand(commandStrings[1]))
                    {
                        throw new Exception("There is no such command: " + line);
                    }

                    if (commandStrings[0] == "END" || commandStrings[0] == "ORG")
                    {
                        // no need to compile line
                        this.byteLength = 0;
                        this.isComment = true;
                    }
                    else
                    {
                        if (commandStrings[1] == "ENDP" || commandStrings[1] == "ENDS")
                        {
                            // no need to compile line
                            this.byteLength = 0;
                            this.isComment = true;
                        }
                        else
                        {
                            // adding new lable
                            SYMTAB.AddLable(commandStrings[0], address);
                            if (commandStrings.Length == 2)
                            {
                                this.commandName = commandStrings[1];
                                this.operands = null;
                            }
                            else
                            {
                                this.commandName = commandStrings[1];
                                this.operands = commandStrings[2].Split(',');
                            }

                            this.Analyse();
                        }
                    }
                }
                else
                {
                    // line is not a reserved function
                    if (commandStrings.Length == 1)
                    {
                        // without arguments
                        this.commandName = commandStrings[0];
                        this.operands = null;
                    }
                    else
                    {
                        // with 2 arguments
                        this.commandName = commandStrings[0];
                        this.operands = commandStrings[1].Split(',');
                    }

                    this.Analyse();
                }
            }
        }

        private string[] getCommandStrings(string command)
        {
            return command.Split(new [] { ' ' }, 3);
        }

        public Int16 LineByteLenght
        {
            get
            {
                return this.byteLength;
            }

            private set
            {
                this.byteLength = value;
            }
        }

        // gets command format and length if it is compilable.
        private void Analyse()
        {
            if (!OPTAB.IsNoCompiledCommand(this.commandName))
            {                
                this.commandFormat = OPTAB.OperandsFormating(this.operands);
                this.w = OPTAB.GetWValue(this.operands);
                this.byteLength = OPTAB.GetFullCommandLenght(this.commandName,this.operands, this.commandFormat, this.w);
            }
            else
            {
                this.byteLength = 0;
            }
        }

        public bool IsComment
        {
            get
            {
                return this.isComment;
            }

            private set
            {
                this.isComment = value;
            }
        }

        public byte[] GetMachineCode()
        {
            if (OPTAB.IsNoCompiledCommand(this.commandName) == false && this.isComment == false)
            {
                return OPTAB.GetMachineCode(commandName, commandFormat, w, operands, byteLength, address);
            }
            else
            {
                return null;
            }
        }
    }
}
